<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Novel2Comic 原型 v1</title>
  <style>
    :root {
      --bg: #0f1115;
      --panel: #171a21;
      --panel-2: #1e2330;
      --text: #e6eaf2;
      --muted: #9aa4b2;
      --accent: #3b82f6;
      --success: #10b981;
      --warn: #f59e0b;
      --danger: #ef4444;
      --border: #2a3140;
    }
    * { box-sizing: border-box; }
    body { margin: 0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, PingFang SC, Noto Sans CJK, Helvetica, Arial, "Microsoft Yahei", sans-serif; background: var(--bg); color: var(--text); }
    header { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; background: var(--panel); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10; }
    header .title { font-weight: 700; letter-spacing: .3px; }
    header nav { display: flex; gap: 10px; }
    header button { background: transparent; color: var(--muted); border: 1px solid var(--border); padding: 6px 10px; border-radius: 8px; cursor: pointer; }
    header button.active { color: var(--text); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }

    .container { padding: 18px; max-width: 1200px; margin: 0 auto; }
    .row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
    .panel { background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }
    .panel h3 { margin: 0 0 12px 0; font-size: 16px; font-weight: 700; }
    .muted { color: var(--muted); font-size: 12px; }

    textarea, input, select { width: 100%; background: #11141b; color: var(--text); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; outline: none; }
    textarea { min-height: 260px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
    .actions { display: flex; gap: 10px; align-items: center; }
    .btn { background: var(--accent); color: #fff; border: none; border-radius: 10px; padding: 10px 14px; cursor: pointer; }
    .btn.ghost { background: transparent; color: var(--muted); border: 1px solid var(--border); }
    .tag { display: inline-block; border: 1px solid var(--border); padding: 2px 8px; border-radius: 999px; font-size: 12px; color: var(--muted); margin-right: 6px; }

    .progress { height: 10px; background: #0e121a; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
    .progress > i { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent), #67e8f9); transition: width .4s ease; }

    table { width: 100%; border-collapse: collapse; }
    th, td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
    th { color: var(--muted); font-weight: 600; }

    .kebab { display: inline-flex; gap: 6px; }
    .kebab .dot { width: 5px; height: 5px; background: var(--muted); border-radius: 50%; display: inline-block; }

    .gallery { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
    .thumb { aspect-ratio: 1 / 1; background: #0f1420; border: 1px solid var(--border); border-radius: 10px; position: relative; display: grid; place-items: center; color: var(--muted); }
    .thumb .badge { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,.5); border: 1px solid var(--border); color: var(--text); font-size: 12px; padding: 2px 6px; border-radius: 999px; }

    .aside { background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 12px; }
    .kv { display: grid; grid-template-columns: 120px 1fr; gap: 6px; font-size: 13px; }

    footer { padding: 30px 18px; color: var(--muted); text-align: center; }

    /* page visibility */
    .page { display: none; }
    .page.active { display: block; }
  </style>
</head>
<body>
  <header>
    <div class="title">Novel2Comic 原型 v1</div>
    <nav>
      <button id="nav-1" class="active" onclick="goPage(1)">1 文本输入与分析</button>
      <button id="nav-2" onclick="goPage(2)">2 文字-画格映射确认</button>
      <button id="nav-3" onclick="goPage(3)">3 生成结果查看</button>
    </nav>
  </header>

  <main class="container">
    <!-- Page 1 -->
    <section id="page-1" class="page active">
      <div class="row">
        <div class="panel">
          <h3>文本/文件上传</h3>
          <p class="muted">支持 TXT / EPUB，自动抽取正文并保留段落编号</p>
          <textarea id="inputText" placeholder="粘贴你的小说文本...\n[12] 他快步冲出校门...\n[13] 傍晚的光线...\n..."></textarea>
          <div style="height:10px"></div>
          <div class="actions">
            <button class="btn" onclick="analyze()">分析</button>
            <button class="btn ghost" onclick="clearText()">清空</button>
            <span id="analyzeHint" class="muted">状态：待分析</span>
          </div>
          <div style="height:10px"></div>
          <div class="progress"><i id="progressBar"></i></div>
        </div>
        <div class="panel">
          <h3>分析结果（示例）</h3>
          <div class="tag">场景：4</div>
          <div class="tag">画格候选：12</div>
          <div class="tag">预计用时：35s</div>
          <div style="height:10px"></div>
          <pre id="previewJson" style="white-space:pre-wrap; margin:0; font-size:12px; color:var(--muted);">等待分析...</pre>
        </div>
      </div>
    </section>

    <!-- Page 2 -->
    <section id="page-2" class="page">
      <div class="panel">
        <h3>文字-画格映射确认</h3>
        <table>
          <thead>
            <tr>
              <th style="width:64px">格#</th>
              <th>关联文本段落</th>
              <th style="width:180px">镜头/角度</th>
              <th style="width:220px">操作</th>
            </tr>
          </thead>
          <tbody id="mappingTable">
            <!-- rows injected by JS -->
          </tbody>
        </table>
        <div style="height:10px"></div>
        <div class="actions">
          <label class="muted">预设风格：</label>
          <select id="stylePreset" style="max-width:200px">
            <option>少年热血</option>
            <option>古风水墨</option>
            <option>赛博霓虹</option>
          </select>
          <label class="muted">强一致性角色：</label>
          <select id="lockRole" style="max-width:200px">
            <option>林一（√）</option>
            <option>苏晴</option>
          </select>
          <span id="validateHint" class="muted">校验：通过（2条提示）</span>
          <span style="flex:1 1 auto"></span>
          <button class="btn" onclick="startGenerate()">生成</button>
        </div>
      </div>
    </section>

    <!-- Page 3 -->
    <section id="page-3" class="page">
      <div class="row">
        <div class="panel">
          <h3>画廊</h3>
          <div id="gallery" class="gallery">
            <!-- thumbs injected -->
          </div>
        </div>
        <div class="aside">
          <h3 style="margin-top:0">详情 / 操作</h3>
          <div class="kv">
            <div class="muted">参数</div>
            <div id="paramBox" class="muted">CFG 6 | 步数 32 | 参考 0.7</div>
            <div class="muted">日志</div>
            <div id="logBox" class="muted">等待任务...</div>
            <div class="muted">审核</div>
            <div id="auditBox" class="muted">-</div>
          </div>
          <div style="height:10px"></div>
          <div class="actions">
            <button class="btn ghost" onclick="resample()">重采样</button>
            <button class="btn ghost" onclick="inpaint()">轻量 Inpaint</button>
            <button class="btn ghost" onclick="editDialog()">改对白/气泡</button>
          </div>
          <div style="height:10px"></div>
          <div class="actions">
            <button class="btn" onclick="exportAs('png')">导出 PNG</button>
            <button class="btn" onclick="exportAs('pdf')">导出 PDF</button>
            <button class="btn" onclick="exportAs('video')">导出短视频</button>
          </div>
        </div>
      </div>
    </section>
  </main>

  <footer>
    Novel2Comic · HTML 原型（依据 docs/Novel2Comic_需求与技术方案_v1.md）
  </footer>

  <script>
    const navButtons = [1,2,3].map(i => document.getElementById(`nav-${i}`));
    function goPage(i){
      document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
      document.getElementById(`page-${i}`).classList.add('active');
      navButtons.forEach((b,idx)=> b.classList.toggle('active', idx === (i-1)));
      if(i===2) mountMapping();
      if(i===3) mountGallery();
    }

    // Page 1 mock analyze
    function clearText(){
      document.getElementById('inputText').value='';
      document.getElementById('analyzeHint').textContent='状态：已清空';
      document.getElementById('progressBar').style.width='0%';
      document.getElementById('previewJson').textContent='等待分析...';
    }
    function analyze(){
      const hint = document.getElementById('analyzeHint');
      const bar = document.getElementById('progressBar');
      hint.textContent = '状态：分析中…';
      let p = 0;
      const t = setInterval(()=>{
        p = Math.min(100, p + Math.random()*22);
        bar.style.width = p+'%';
        if(p >= 100){
          clearInterval(t);
          const json = {
            panels: [
              { id:'p1', sourceParagraphIds:[12,13], shot:'medium', angle:'eye_level', setting:'校门口黄昏', characters:[{name:'林一',emotion:'紧张',action:'小跑'}], dialog:[{speaker:'林一',text:'要迟到了！'}] },
              { id:'p2', sourceParagraphIds:[14], shot:'close', angle:'low', setting:'校门口', characters:[{name:'林一',emotion:'着急'}], dialog:[{speaker:'林一',text:'快点！'}] },
              { id:'p3', sourceParagraphIds:[15,16], shot:'long', angle:'high', setting:'宿舍走廊', characters:[], dialog:[] }
            ],
            global: { targetPanelCount: 12 }
          };
          document.getElementById('previewJson').textContent = JSON.stringify(json, null, 2);
          hint.textContent = '完成：切分 4 场景 / 12 画格候选。点击顶部“2 映射确认”。';
        }
      }, 400);
    }

    // Page 2 mapping
    function mountMapping(){
      const data = [
        { idx:1, paras:[12,13], shot:'中景 / 平视' },
        { idx:2, paras:[14], shot:'近景 / 低角' },
        { idx:3, paras:[15,16], shot:'远景 / 俯视' }
      ];
      const tbody = document.getElementById('mappingTable');
      tbody.innerHTML = '';
      data.forEach(row => {
        const tr = document.createElement('tr');
        tr.innerHTML = `
          <td>0${row.idx}</td>
          <td>[${row.paras.join('][')}] <span class="muted">…片段摘要…</span></td>
          <td>${row.shot}</td>
          <td>
            <span class="kebab" title="合并/拆分/参数">
              <i class="dot"></i><i class="dot"></i><i class="dot"></i>
            </span>
            <button class="btn ghost" style="margin-left:8px">上移</button>
            <button class="btn ghost">下移</button>
            <button class="btn ghost" style="color: var(--danger)">删除</button>
          </td>`;
        tbody.appendChild(tr);
      });
      document.getElementById('validateHint').textContent = '校验：通过（对白/气泡阈值正常）';
    }
    function startGenerate(){
      goPage(3);
      document.getElementById('logBox').textContent = '提交生成任务… 阶段1：草图';
      setTimeout(()=>{
        document.getElementById('logBox').textContent = '阶段2：一致性图生图';
      }, 1200);
      setTimeout(()=>{
        document.getElementById('logBox').textContent = '阶段3：细化/超分 完成';
        document.getElementById('auditBox').textContent = '通过（0 风险）';
        mountGallery(true);
      }, 2600);
    }

    // Page 3 gallery
    function mountGallery(done){
      const g = document.getElementById('gallery');
      g.innerHTML = '';
      const count = 12;
      for(let i=1;i<=count;i++){
        const d = document.createElement('div');
        d.className = 'thumb';
        d.innerHTML = `<span class="badge">P${String(i).padStart(2,'0')}</span>预览`;
        g.appendChild(d);
      }
      if(done){ document.getElementById('paramBox').textContent = 'CFG 6 | 步数 32 | 参考 0.7 | 风格：少年热血'; }
    }

    // Actions on page 3
    function resample(){ alert('已触发重采样队列（示例）'); }
    function inpaint(){ alert('打开轻量 Inpaint（示例）'); }
    function editDialog(){ alert('打开对白/气泡编辑（示例）'); }
    function exportAs(type){ alert('导出 ' + type.toUpperCase() + '（示例）'); }
  </script>
</body>
</html>
